import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

//package com.rungeek.sqlite;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.net.*;

import org.eclipse.swt.widgets.Display;

public class ClientLogic {
    public class friendInfo {
        public friendInfo (String username, String ip, int port_no){
            userID = username;
            if (ip.equals("") || ip.equals("null") || ip.equals("NULL")) {
            	status = "offline";
            }
            else {
            	status = "online";
            }
            IP = ip;
            port = port_no;
        }

        public boolean chatting = false;
        public String userID;
        public String status;
        public String IP;
        public int port;
        public chatwindow window;
    }



    //Singleton
    protected static String serverIP;
    protected static final int serverport = 9000;
    protected static String _username;
    protected static friendlist fl;
    protected static login loginwindow;
    protected static Queue<String> receivedMessages;
    protected static HashMap<String, friendInfo> friends;
    private static ClientLogic single = null;

    protected ClientLogic() {
        //Creation -- protected, only run once
        receivedMessages = new LinkedList<String>();  
        friends = new HashMap<String, friendInfo>();
    }

    public static ClientLogic getInstance() {
        if(single==null) {
            single = new ClientLogic();
        }
        return single;
    }

    /**
     * @param args
     */
    public static void main(String[] args)
    {
        //Start listener thread
        try {
        	listener = new ClientListener();
        	Thread t = new Thread(listener);
        	t.start();
        } catch(IOException e) {
        	System.err.println("Could not open socket.");
        	System.exit(-5);
        }
        
        try {
            getInstance();
            loginwindow = new login();
            loginwindow.open();
        } 
        catch (Exception e) {
            e.printStackTrace();
        }
        // TODO Auto-generated method stub
    }

    public boolean isFriend(String userName) {
        return friends.containsKey(userName);
    }

    private boolean exists(String username) {
        try {
            String result = sendServerMessage("exists", username, "");
            return result.equals("true");
        } catch (IOException e) {
            return false;
        }
    }

    public boolean addFriend(String Username) {
        //create tcp connection with server
        if (!exists(Username)) return false;
        ArrayList<String> params = new ArrayList<String>();
        params.add(_username);
        params.add(Username);
        ArrayList<String> result;
        try {
            result = sendServerMessage("addfriend", params, 4);
        } catch (IOException e) {
            return false;
        }
        
        if(result.get(0).equals("false")) return false;
        
        //sends message to retrieve online status (could be done as part of message above)
        //add username to friend list
        //return true if successful, else false
        String User = result.get(1);
        String ip = result.get(2);
        int port = 0;
        if (!result.get(3).equals("NULL"))
        {
        	port = Integer.parseInt(result.get(3));
        }
        friendInfo newFriend = new friendInfo(Username, ip, port);
        friends.put(Username, newFriend);
        fl.addFriend(User, newFriend.status);
        return true;
    } 

    public boolean removeFriend(String Username) {
        //create tcp connection with server
        //sends message to server to remove relation from Friends database
        //remove username from friend list
        //close tcp connection
        String result;
        try {
            result = sendServerMessage("removefriend", _username, Username);
        } catch (IOException e) {
            return false;
        }
        
        if(result.equals("false")) return false;
            
        friends.remove(Username);
        fl.removeFriend(Username);
        return true;
        //return true if successful, else false
    }

    public String createUser(String Username, String Password, String IP /*, int portNo*/) throws IOException {
        serverIP = IP;
        String result = "";
        //port = portNo;
        //create tcp connection with server
        if (!exists(Username)){
            //tell server to add User to database
            // create local database (don't need to ask server for friends since new account)
            //close tcp conn
            ArrayList<String> params = new ArrayList<String>();
            params.add(Username);
            params.add(Password);
            params.add(IP);
            result = sendServerMessage("register", params);

            return result;
        }
        //close tcp conn
        //_username = Username;
        //return true if successful, else false
        return "This user name is taken.";
    }

    public boolean changePassword(String password) throws IOException {
        //tell server to change password for this user
        String result = sendServerMessage("changepwd", _username, password);
        //return true if successful, else false
        return result.equals("true");
    }

    public String login(String username, String password, String IP/*, int portNo*/) throws IOException {
        serverIP = IP;
        //port = portNo;
        //send login info to server and get response
        //return true if login is successful, else false
        ArrayList<String> params = new ArrayList<String>();
        params.add(username);
        params.add(password);
        params.add(String.valueOf(localPort));
        ArrayList<String> feedback = sendServerMessage("login", params, -1);
        if (feedback.get(0).equals("Userlist")) {
            _username = username;
            loginwindow.shell.close();

            fl = new friendlist();
            for (int i = 1; i < feedback.size(); i += 3) {
                    String User = feedback.get(i);
                    String ip = feedback.get(i+1);
                    int port = 0;
                    if (!feedback.get(i+2).equals("null"))
                    {
                    	port = Integer.parseInt(feedback.get(i+2));
                    }
                    friendInfo newFriend = new friendInfo(User, ip, port);
                    friends.put(User, newFriend);
                    String status;
                    if (ip.equals("") || ip.equals("null") || ip.equals("NULL")) {
                            status = "offline";
                    }
                    else {
                            status = "online";
                    }
                    fl.addFriend(User, status);
            }
            fl.open();
            return "";
        } else
          return feedback.get(1);
    }

    public void logout() throws IOException{
        //send message to server telling it you are logging off
        ArrayList<String> params = new ArrayList<String>();
        params.add(_username);
        sendServerMessage("logout", params);
        friends = new HashMap<String, friendInfo>();
        fl.shell.close();
    }

    /*void listener_loop ( void* param ) {
        while(1) {
            //String pkt = ###Grab packet 
            //receive_queue.push_back(pkt);
        }
    }*/
    
    public void receiveChatMessage(String Username, String Message)
    { 
    	friendInfo fi = friends.get(Username);
    	if (fi == null)
    	{
    		//friends.put(Username, new friendInfo(Username, ));
    	}
    	else if (!fi.chatting)
    	{
    		chatwindow cw = new chatwindow(Username, Message);
    		Display.getDefault().asyncExec(cw);
    	}
    	else
    	{
    		UpdateChatWindow cw = new UpdateChatWindow(Username, Message);
    		Display.getDefault().asyncExec(cw);
    	}
    }

    public String getUsername(){
        return _username;
    }

    
    
    //Adapter function -- sends Message with 2 params to server expecting 1 response
    public String sendServerMessage(String message, String p1, String p2) throws IOException {
        ArrayList<String> params = new ArrayList<String>();
        params.add(p1);
        params.add(p2);
        params = sendServerMessage(message, params, 1);
        return params.get(0);
    }
    
    public String sendServerMessage(String message,
            ArrayList<String> params) throws IOException {
    	ArrayList<String> res = sendServerMessage(message, params, 1);
    	return res.get(0);
    }
    
    //Adapter -- default to 1 return value (string)
    public ArrayList<String> sendServerMessage(String message,
            ArrayList<String> params, int returnCount)    throws IOException {
        Socket _socket = null;
        PrintWriter out = null;
        BufferedReader in = null;
        try {
            _socket = new Socket(serverIP, serverport);
            out = new PrintWriter(_socket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
        } catch (UnknownHostException e) {
            System.err.println("Cannot resolve host");
            System.exit(1);
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for the connection.");
            System.exit(1);
        }

        out.println(message);
        
        for(int i = 0; i<params.size(); ++i)
            out.println(params.get(i));
        
        out.println("{}");//EOF message
        //out.close();
        String rtrn;
        ArrayList<String> returnVals = new ArrayList<String>();
        for(int i=0; i<returnCount; ++i) {
	        rtrn = in.readLine();
	        if (rtrn != null) {
	            returnVals.add(rtrn);
        	}
        }
        
        if(returnCount < 0) {
                while((rtrn = in.readLine())!=null) {
                        returnVals.add(rtrn);
                        //if (rtrn == null)
                        //	System.out.println("actually null");
                        //else if (rtrn.isEmpty())
                        //	System.out.println("empty string");
                        //else
                        	System.out.println(rtrn);
                }
        }
        
        in.close();
        _socket.close();
        return returnVals;
    }
    
    public void online(String user, String IP, int port)
    {
    	if (friends.get(user) == null)
    	{
    		friendInfo fi = new friendInfo(user, IP, port);
    		friends.put(user, fi);
    		
    		UpdateFriendsList w = new UpdateFriendsList(user, true);
    		Display.getDefault().asyncExec(w);
    	}
    	else
    	{
    		friends.get(user).IP = IP;
    		friends.get(user).port = port;
    		friends.get(user).status = "online";
    		
    		UpdateFriendsList w = new UpdateFriendsList(user, true);
    		Display.getDefault().asyncExec(w);
    	}
    }

    public void offline(String user)
    {
    	if (friends.get(user) == null)
    	{
    		friendInfo fi = new friendInfo(user, "null", 0);
    		friends.put(user, fi);
    		
    		UpdateFriendsList w = new UpdateFriendsList(user, false);
    		Display.getDefault().asyncExec(w);
    	}
    	else
    	{
    		friends.get(user).IP = "null";
    		friends.get(user).port = 0;
    		friends.get(user).status = "offline";
    		
    		UpdateFriendsList w = new UpdateFriendsList(user, false);
    		Display.getDefault().asyncExec(w);
    	}
    }

    public void sendMessage(String clientID, String text) throws IOException {
        Socket sckt = null;
        String _ip_addr = friends.get(clientID).IP;
        int port = friends.get(clientID).port;
        System.out.println("IP : " + _ip_addr + "    port: " + port);
        sckt = new Socket(_ip_addr, port);
        PrintWriter out = new PrintWriter(sckt.getOutputStream(), true);
        out.println("message");
        out.println(_username);
        out.println(text);
       	out.close();
    }

    public void initializeChat(String Username){
    	if (friends.get(Username).status.equals("online")){
	    	if (friends.get(Username).chatting == false){
	            new chatwindow(Username).open(Username, "");
	            friends.get(Username).chatting = false;
	        }
    	}
    }

    public HashMap<String, String> getFriendList()
    { 
        HashMap<String, String> temp = new HashMap<String, String>();
        Collection<friendInfo> c = friends.values();
        Iterator<friendInfo> itr = c.iterator();
        while(itr.hasNext())
        {
            friendInfo tempFriend = (friendInfo)itr.next();
            temp.put(tempFriend.userID, tempFriend.status);
        }
        return temp;
    }
    
    private int lastSocket = 0;
    private static ClientListener listener= null;
    public static int localPort = 0;
}
